在講Test Double以前,得先上搞清楚另外兩個
SUT:System Under Test/Software Under Test 【待測程式】,在Unit Test角度下待測程式會是method
DOC:Depended-on Component【相依元件】,例如:成立訂單函數,如果訂單失敗異常會寫入log,log函數就是成立訂單函數的DOC
在實務上筆者在寫單元測試的時候,因為SUT會去呼叫其他的物件也會呈現出SUT依賴DOC,
在測試SUT勢必會有DOC存在,搞得測試很複雜。
目前測試替身有五種Dummy Object、Test Stub、Test Spy、Fake Object與Mock Object
筆者目前是常用到Stub、Mock、Fake Object。
Dummy Objects用法:測試中必續傳入的物件,實際上這些物件根本不會用到,只是為了能夠調用背側物件而必須傳入的一個東西【目前沒用到】。
Stub用法:回傳固定的值。
public class StubUserRepository : IUserRepository
{
public string GetUserRole(string username)
{
return "administrator";
}
public List<User> GetAllUsers()
{
return new List<User>()
{
new User{ Role = "administrator", Name = "admin"},
};
}
}
public interface IUserRepository
{
string GetUserRole(string username);
}
public class User
{
public string Name { get; set; }
public string Role { get; set; }
}
Fake用法:接近提供原始物件較簡單實作方式。
public class FakeUserRepository : IUserRepository
{
public string GetUserRole(string username)
{
if (username == "admin")
return "administrator";
else
return "contributor";
}
}
public interface IUserRepository
{
string GetUserRole(string username);
}
Spy用法:把spy想成是一個偷偷紀錄呼叫過程的Stub,宣告被spy的物件,某個函式被呼叫的時候回傳某個值。
public class SpyUserStore : IUserRepository
{
private static int Counter { get; set; }
public SpyUserStore()
{
Counter = 0;
}
public string GetUserRole(string username)
{
if (Counter >= 1)
throw new Exception("Function called more than once");
Counter++;
if (username == "admin")
return "administrator";
else
return "contributor";
}
}
Mock用法:用動態程式方式、提供類似Dummy,Stub,Spy功能
var _memberRepository = Substitute.For<IMemberRepository>();
var _memberService = new MemberService(_memberRepository);
var _setReturnMember = new Member()
{
UserId = "test001",
Password = "test001",
Name = "test",
Email = "abc@gmail.com"
};
_memberRepository.GetByMember(userid,password).Returns(_setReturnMember);
最後總結一下,測試替身可以解決單元測試的那些問題?
另外其實在真的寫測試,也不用太在意你是用哪一個測試替身...
參考來源